package com.example.serverdemo.netty.device_tcp_server.function_module;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import com.example.serverdemo.netty.device_tcp_server.handler.DeviceHandler;
import com.example.serverdemo.utils.crcutls.CRCUtil;
import com.example.serverdemo.utils.log.PrintLogUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import static com.example.serverdemo.common.Constant.*;
import static com.example.serverdemo.common.Constant.DEFAULT_CRC_PLACEHOLDER;

public abstract class BaseFunctionModule {
    protected DeviceHandler handler;
    private Timer overTimeDetectionTask;
    protected boolean timeoutStatus;
    protected void init(){
        this.timeoutStatus = false;
    }
    public void unBindDeviceHandler(){
        this.handler = null;
    }
    public void bindDeviceHandler(DeviceHandler handler){
        this.handler = handler;
    }
    protected boolean sendOrder(String order, List<String> params,byte mark, byte address, boolean isStartTimeout){
        try {
            ArrayList<Byte> data = new ArrayList();
            if (order != null){
                for (byte value : order.getBytes()) {
                    data.add(value);
                }
            }
            if (params != null){
                for (String param : params) {
                    data.add(Convert.intToByte(0x20));
                    for (byte value : param.getBytes()) {
                        data.add(value);
                    }
                }
            }
            if (params != null && params.size() > 0){
                data.add(Convert.intToByte(0x00));
            }
            byte[] dataSize = Convert.intToBytes(data.size());
            byte[] packageHead = new byte[]{FLAG1,FLAG2,address,dataSize[3],dataSize[2],mark,DEFAULT_CRC_PLACEHOLDER,DEFAULT_CRC_PLACEHOLDER};
            byte[] writeBuff = ArrayUtil.addAll(packageHead, Convert.toPrimitiveByteArray(data));
            byte[] crcBuff = calculationCRC(writeBuff);
            if (crcBuff == null) return false;
            writeBuff[6] = crcBuff[3];
            writeBuff[7] = crcBuff[2];
            this.handler.writeAndFlush(writeBuff);
            if (isStartTimeout) this.startOverTimeDetectionTask();
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    protected boolean sendData(byte[] data, byte mark, byte address,boolean isStartTimeout) {
        try {
            byte[] dataSize = Convert.intToBytes(data.length);
            byte[] packageHead = new byte[]{FLAG1,FLAG2,address,dataSize[3],dataSize[2],mark,DEFAULT_CRC_PLACEHOLDER,DEFAULT_CRC_PLACEHOLDER};
            byte[] writeBuff = ArrayUtil.addAll(packageHead, data);
            byte[] crcBuff = calculationCRC(writeBuff);
            if (crcBuff == null) return false;
            writeBuff[6] = crcBuff[3];
            writeBuff[7] = crcBuff[2];
            this.handler.writeAndFlush(writeBuff);
            if (isStartTimeout) this.startOverTimeDetectionTask();
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    protected byte[] calculationCRC(byte[] data){
        byte[] crc = null;
        try {
            crc = Convert.intToBytes(CRCUtil.getInstance().calcCrc16(data));
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return crc;
    }
    protected boolean startOverTimeDetectionTask(){
        try {
            this.timeoutStatus = true;
            this.overTimeDetectionTask = new Timer();
            this.overTimeDetectionTask.schedule(new TimerTask() {
                @Override
                public void run() {
                    if (timeoutStatus){
                        failed(handler.deviceId + " OverTime");
                    }
                }
            }, COMMUNICATION_TIMEOUT_COUNT);
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    protected boolean stopOverTimeDetectionTask(){
        try {
            this.timeoutStatus = false;
            if (this.overTimeDetectionTask != null){
                this.overTimeDetectionTask.cancel();
                this.overTimeDetectionTask = null;
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    protected abstract boolean success(Object obj);
    protected abstract boolean failed(String msg);
    public abstract boolean msgDistributionTransactionManagement(byte[] msg);
}
